package com.test.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.upload.FormFile;

/**
 * 文件工具流. <br>
 * 对文件的基本操作.
 * <p>
 * Copyright: Copyright (c) Nov 10, 2010 10:22:56 AM
 * <p>
 * Company: 北京宽连十方数字技术有限公司
 * <p>
 * 
 * @author suncg@c-platform.com
 * @version 1.0.0
 */
public class FileUtils {

	private static Log log = LogFactory.getLog(FileUtils.class);

	/**
	 * 关闭写字节流<br>
	 * 
	 * @param is
	 * @throws IOException
	 */
	public static void close(InputStream is) {
		try {
			if (is != null) {
				is.close();
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("close(InputStream is):失败 " + FormatExceptionStack.formatException(e));
		}
	}

	/**
	 * 关闭读字节流<br>
	 * 
	 * @param os
	 * @throws IOException
	 */
	public static void close(OutputStream os) {
		try {
			if (os != null) {
				os.close();
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("close(OutputStream os):失败 " + FormatExceptionStack.formatException(e));
		}
	}

	/**
	 * 关闭读字符流<br>
	 * 
	 * @param reader
	 * @throws IOException
	 */
	public static void close(Reader reader) {
		try {
			if (reader != null) {
				reader.close();
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("close(Reader reader):失败 " + FormatExceptionStack.formatException(e));
		}
	}

	/**
	 * 关闭写字符流<br>
	 * 
	 * @param writer
	 * @throws IOException
	 */
	public static void close(Writer writer) {
		try {
			if (writer != null) {
				writer.close();
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("close(Writer writer):失败 " + FormatExceptionStack.formatException(e));
		}
	}

	/**
	 * 复制整个文件夹内容<br>
	 * 
	 * @param oldPath
	 *            旧文件路径
	 * @param newPath
	 *            新文件路径
	 * @param bufferSize
	 *            字节缓冲区大小
	 * @exp copyFolder( "e:/测试文件/css" , "e:/css/" );
	 * @return boolean
	 * @throws IOException
	 */
	public static void copyFolder(String oldPath, String newPath, int bufferSize) throws IOException {
		File newFile = getFile(newPath);
		if (!newFile.exists()) {
			newFile.mkdirs(); // 如果文件夹不存在 则建立新文件夹
		}
		File oldFile = getFile(oldPath);
		String[] file = oldFile.list();
		File temp = null;
		for (int i = 0; i < file.length; i++) {
			// 文件分隔符,各个操作系统不一样 如WIndows的是"\",而Unix的是"/"
			if (oldPath.endsWith(File.separator)) {
				temp = new File(oldPath + file[i]);
			} else {
				temp = new File(oldPath + File.separator + file[i]);
			}
			if (temp.isFile()) {
				FileInputStream input = new FileInputStream(temp);
				FileOutputStream output = new FileOutputStream(newPath + "/" + (temp.getName()).toString());
				byte[] b = new byte[bufferSize];
				int len;
				while ((len = input.read(b)) != -1) {
					output.write(b, 0, len);
				}
				flush(output);
				close(output);
				close(input);
			}
			if (temp.isDirectory()) {// 如果是子文件夹
				copyFolder(oldPath + "/" + file[i], newPath + "/" + file[i], bufferSize);
			}
		}
	}

	/**
	 * 删除目录（文件夹）以及目录下的文件<br>
	 * 
	 * @param dir
	 *            被删除目录的文件路径
	 * @return 目录删除成功返回true,否则返回false
	 */
	public static boolean deleteDirectory(String dir) {
		// 如果dir不以文件分隔符结尾，自动添加文件分隔符
		if (!dir.endsWith(File.separator)) {
			dir = dir + File.separator;
		}
		File dirFile = new File(dir);
		// 如果dir对应的文件不存在，或者不是一个目录，则退出
		if (!dirFile.exists() || !dirFile.isDirectory()) {
			return false;
		}
		boolean flag = true;
		// 删除文件夹下的所有文件(包括子目录)
		File[] files = dirFile.listFiles();
		for (int i = 0; i < files.length; i++) {
			// 删除子文件
			if (files[i].isFile()) {
				flag = deleteFile(files[i].getAbsolutePath());
				if (!flag) {
					break;
				}
			}
			// 删除子目录
			else {
				flag = deleteDirectory(files[i].getAbsolutePath());
				if (!flag) {
					break;
				}
			}
		}

		if (!flag) {
			return false;
		}

		// 删除当前目录
		if (dirFile.delete()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 删除单个文件<br>
	 * 
	 * @param fileName
	 *            被删除文件的文件名
	 * @return 单个文件删除成功返回true,否则返回false
	 */
	public static boolean deleteFile(String fileName) {
		File file = new File(fileName);
		if (file.isFile() && file.exists()) {
			file.delete();
			return true;
		} else {
			log.debug("删除文件失败：文件不存在");
			return false;
		}
	}

	/**
	 * 根据路径删除单个文件<br>
	 */
	public static void deleteFileByDir(String dir) {
		File temp = new File(dir);
		if (temp.isFile()) {
			temp.delete();
		}
	}

	/**
	 * 将文件流转换成字符串<br>
	 * 
	 * @param filePath
	 *            文件路径
	 * @param encoding
	 *            编码
	 * @param bufferSize
	 *            缓冲字符数组大小
	 * @return
	 * @throws IOException
	 */
	public static String file2String(String filePath, String encoding, int bufferSize) throws IOException {
		InputStreamReader reader = null;
		StringWriter writer = new StringWriter();
		try {
			File file = getFile(filePath);
			reader = new InputStreamReader(new FileInputStream(file), encoding);
			char[] buffer = new char[bufferSize];
			int n = 0;
			while ((n = reader.read(buffer)) != -1) {
				writer.write(buffer, 0, n);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
			log.error("file2String:失败 " + FormatExceptionStack.formatException(e));
			throw new IOException("文件转换到字符串失败");
		}
		finally {
			flush(writer);
			close(reader);
			close(writer);
		}
		return writer.toString();
	}

	/**
	 * 文件上传<br>
	 * 
	 * @param file
	 *            文件
	 * @param fileName
	 *            文件名
	 * @param path
	 *            路径
	 * @throws IOException
	 */
	public static void fileUpload(File file, String fileName, String path) throws IOException {
		InputStream is = null;
		OutputStream os = null;
		try {
			is = new FileInputStream(file);
			File destFile = getFile(path + fileName);
			os = new FileOutputStream(destFile);
			byte[] buffer = new byte[400];// 缓冲设置
			int length = 0;
			while ((length = is.read(buffer)) > 0) {
				os.write(buffer, 0, length);
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("fileUpload:失败 " + FormatExceptionStack.formatException(e));
			throw new IOException("文件保存失败");
		}
		finally {
			flush(os);
			close(is);
			close(os);
		}
	}

	/**
	 * 多文件上传<br>
	 * 
	 * @param file
	 *            文件集
	 * @param fileName
	 *            文件名称集
	 * @param contentType
	 *            类型
	 * @param path
	 *            路径
	 * @throws IOException
	 */
	public static void fileUploads(List<File> file, List<String> fileName, List<String> contentType, String path) throws IOException {
		for (int i = 0; i < file.size(); ++i) {
			InputStream is = new FileInputStream(file.get(i));
			File destFile = new File(path, FileUtils.getFileNewName(fileName.get(i)));
			OutputStream os = new FileOutputStream(destFile);
			byte[] buffer = new byte[400];
			int length = 0;
			while ((length = is.read(buffer)) > 0) {
				os.write(buffer, 0, length);
			}
			flush(os);
			close(is);
			close(os);
		}
	}

	/**
	 * 刷新写字节流<br>
	 * 
	 * @param os
	 */
	public static void flush(OutputStream os) {
		try {
			if (os != null) {
				os.flush();
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("flush(OutputStream os):失败 " + FormatExceptionStack.formatException(e));
		}
	}

	/**
	 * 刷新写字符流<br>
	 * 
	 * @param writer
	 */
	public static void flush(Writer writer) {
		try {
			if (writer != null) {
				writer.flush();
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("flush(Writer writer):失败 " + FormatExceptionStack.formatException(e));
		}
	}

	/**
	 * 获取文件对象<br>
	 * 
	 * @param filePath
	 *            文件路径
	 * @return
	 * @throws IOException
	 */
	public static File getFile(String filePath) throws IOException {
		File file = null;
		try {
			if (filePath == null) {
				return null;
			}
			file = new File(filePath);
			if (!file.exists()) {
				if (!file.getParentFile().exists()) {
					file.getParentFile().mkdirs();
				}
				file.createNewFile();
			}
		}
		catch (Exception e) {
			e.printStackTrace();
			log.error("getFile:失败 " + FormatExceptionStack.formatException(e));
			throw new IOException("获取文件对象失败");
		}
		return file;
	}

	/**
	 * 用日期时间戳来命名文件名称<br>
	 * 
	 * @param fileName
	 *            原文件名称
	 * @return 12321443432423.suffix
	 */
	public static String getFileNewName(String fileName) {
		if (StringUtils.equals(fileName, null)) {
			return "";
		}
		String suffix = StringUtils.substring(fileName, StringUtils.lastIndexOf(fileName, ".") + 1, fileName.length());
		StringBuilder sb = new StringBuilder();
		sb.append(DateUtils.longToDateString(System.currentTimeMillis(), "yyyyMMddHHmmss")).append('.').append(suffix);
		return sb.toString();
	}

	/**
	 * 获取文件后缀名<br>
	 * 
	 * @param fileName
	 *            文件名
	 * @return
	 */
	public static String getFileSuffix(String fileName) {
		if (StringUtils.equals(fileName, null)) {
			return "";
		}
		String suffix = StringUtils.substring(fileName, StringUtils.lastIndexOf(fileName, ".") + 1, fileName.length());
		return suffix;
	}

	public static String getPathFileSuffix(File file) {
		if (file == null) {
			return null;
		}
		return getFileSuffix(file.getName());
	}

	/**
	 * 获取路径下文件后缀名<br>
	 * 
	 * @param filePath
	 *            文件路径 如：e:/test.txt
	 * @return
	 * @throws IOException
	 */
	public static String getPathFileSuffix(String filePath) throws IOException {
		return getPathFileSuffix(getFile(filePath));
	}

	/**
	 * 通过路径读取对象流<br>
	 * 
	 * @param path
	 *            写到的文件路径
	 * @exp write("e:/测试文件/test.dat", map);
	 * @return
	 * @throws Exception
	 * @throws Exception
	 */
	public static Object readObjectFfile(String path) throws Exception {
		Object object = null;
		ObjectInputStream in = null;
		try {
			in = new ObjectInputStream(new FileInputStream(getFile(path)));
			object = in.readObject();
		}
		catch (Exception e) {
			e.printStackTrace();
			log.error("readObjectFfile:失败 " + FormatExceptionStack.formatException(e));
			throw new Exception("从文件读取对象流失败");
		}
		finally {
			close(in);
		}
		return object;
	}

	/**
	 * 保存一个文件<br>
	 * 
	 * @throws IOException
	 */
	public static boolean saveFile(String filePath, byte[] b) throws IOException {
		OutputStream fos = null;
		try {
			File file = getFile(filePath);
			if (file == null) {
				return false;
			}
			fos = new FileOutputStream(file);
			fos.write(b);
			return true;
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("saveFile:失败 " + FormatExceptionStack.formatException(e));
			throw new IOException("文件保存失败");
		}
		finally {
			flush(fos);
			close(fos);
		}
	}

	/**
	 * 保存一个文件<br>
	 * 
	 * @throws IOException
	 */
	public static boolean saveFile(String filePath, String content) throws IOException {
		FileWriter writer = null;
		try {
			File file = getFile(filePath);
			if (file == null) {
				return false;
			}
			writer = new FileWriter(file);
			writer.write(content);
			return true;
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("saveFile:失败 " + FormatExceptionStack.formatException(e));
			throw new IOException("保存文件失败");
		}
		finally {
			flush(writer);
			close(writer);
		}
	}

	/**
	 * 将字符串转换为文件流<br>
	 * 
	 * @param content
	 *            字符串内容
	 * @param path
	 *            文件路径
	 * @param encoding
	 *            编码
	 * @throws IOException
	 * @throws IOException
	 */
	public static void string2file(String content, String path, String encoding) throws IOException {
		BufferedReader reader = null;
		OutputStreamWriter writer = null;
		try {
			reader = new BufferedReader(new StringReader(content));
			writer = new OutputStreamWriter(new FileOutputStream(path), encoding);
			int len;
			char buf[] = new char[1024];
			while ((len = reader.read(buf)) != -1) {
				writer.write(buf, 0, len);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
			log.error("string2file:失败 " + FormatExceptionStack.formatException(e));
			throw new IOException("字符串转换到文件失败");
		}
		finally {
			flush(writer);
			close(reader);
			close(writer);
		}
	}

	/**
	 * 解压zip文件<br>
	 * 
	 * @param fileName
	 *            将要被解压的文件
	 * @param filePath
	 *            解压到的路径
	 * @param bufferSize
	 *            缓冲字节数组大小
	 * @exp z.zip("E:\\测试文件\\zip\\myfiles.zip", "E:\\测试文件\\zip\\");
	 * @throws IOException
	 */
	@SuppressWarnings("unchecked")
	public static void unzip(String fileName, String filePath, int bufferSize) throws IOException {
		ZipFile zipFile = new ZipFile(fileName);
		Enumeration emu = zipFile.entries();
		while (emu.hasMoreElements()) {
			ZipEntry entry = (ZipEntry) emu.nextElement();
			if (entry.isDirectory()) {
				new File(filePath + entry.getName()).mkdirs();
				continue;
			}
			BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));
			File file = getFile(filePath + entry.getName());
			File parent = file.getParentFile();
			if (parent != null && (!parent.exists())) {
				parent.mkdirs();
			}
			FileOutputStream fos = new FileOutputStream(file);
			BufferedOutputStream bos = new BufferedOutputStream(fos, bufferSize);
			int count;
			byte data[] = new byte[bufferSize];
			while ((count = bis.read(data, 0, bufferSize)) != -1) {
				bos.write(data, 0, count);
			}
			flush(bos);
			close(bos);
			close(bis);
		}
		// FIXME
		zipFile.close();
	}

	public static String upload(String dir, FormFile formFile) throws Exception {

		Date date = new Date();

		String fname = formFile.getFileName();

		// 取文件的后缀

		int i = fname.lastIndexOf(".");

		String type = fname.substring(i + 1);

		// 用时间给文件赋新名称

		// String name = String.valueOf(date.getTime());
		String name = String.valueOf(System.nanoTime());

		fname = name + "." + type;

		InputStream streamIn = formFile.getInputStream(); // 创建读取用户上传文件的对象

		String yearStr = new SimpleDateFormat("yyyy").format(new Date());
		String monthStr = new SimpleDateFormat("MM").format(new Date());
		String dayStr = new SimpleDateFormat("dd").format(new Date());
		String rPath = yearStr + "/" + monthStr + "/" + dayStr + "/";
		File uploadFile = new File(dir + "/" + rPath); // 创建把上传数据写到目标文件的对象

		// 判断指定路径是否存在，不存在则创建路径

		if (!uploadFile.exists() || uploadFile == null) {

			uploadFile.mkdirs();

		}

		String path = uploadFile.getPath() + "/" + fname;

		OutputStream streamOut = new FileOutputStream(path);

		int bytesRead = 0;

		byte[] buffer = new byte[8192];

		while ((bytesRead = streamIn.read(buffer, 0, 8192)) != -1) {
			streamOut.write(buffer, 0, bytesRead);

		}

		streamOut.close();

		streamIn.close();

		formFile.destroy();
		return "/upload/" + rPath + fname;

	}

	/**
	 * ftp上传文件到文件服务器。 <br>
	 * <p>
	 * 
	 * @param dir
	 *            上传路径
	 * @param formFile
	 *            文件
	 * @param websiteId
	 *            网站id
	 * @return
	 * @throws Exception
	 * @return 文件保存相对路径
	 * @throws Exception
	 *             异常说明
	 */
	public static String upload(String dir, FormFile formFile, long websiteId) throws Exception {

		Date date = new Date();

		String fname = formFile.getFileName();

		// 取文件的后缀

		int i = fname.lastIndexOf(".");

		String type = fname.substring(i + 1);

		// 用时间给文件赋新名称

		String name = String.valueOf(date.getTime());

		fname = name + "." + type;

		// Long websiteId = SessionUtil.getCurrentWebsiteFromSession(request);
		InputStream streamIn = formFile.getInputStream(); // 创建读取用户上传文件的对象

		String yearStr = new SimpleDateFormat("yyyy").format(new Date());
		String monthStr = new SimpleDateFormat("MM").format(new Date());
		String dayStr = new SimpleDateFormat("dd").format(new Date());
		String rPath = yearStr + "/" + monthStr + "/" + dayStr + "/";
		String fileUploaderDir = dir + "/" + rPath;
		File uploadFile = new File(fileUploaderDir); // 创建把上传数据写到目标文件的对象

		// 判断指定路径是否存在，不存在则创建路径

		if (!uploadFile.exists() || uploadFile == null) {

			uploadFile.mkdirs();

		}

		String path = uploadFile.getPath() + "/" + fname;

		OutputStream streamOut = new FileOutputStream(path);

		int bytesRead = 0;

		byte[] buffer = new byte[8192];

		while ((bytesRead = streamIn.read(buffer, 0, 8192)) != -1) {

			streamOut.write(buffer, 0, bytesRead);

		}

		String ftpPath = SFTPTool.ftp(fileUploaderDir, rPath, fname);

		streamOut.close();

		streamIn.close();

		formFile.destroy();

		return ftpPath;

	}

	/**
	 * 将对象map集合写进文件<br>
	 * 
	 * @param path
	 *            读取文件路径
	 * @param map
	 *            对象map集
	 * @throws IOException,
	 *             FileNotFoundException
	 * @exp read("e:/测试文件/test.dat");
	 * @throws Exception
	 */
	public static void writeObjectTfile(String path, Object map) throws IOException, FileNotFoundException {
		ObjectOutputStream out = null;
		try {
			out = new ObjectOutputStream(new FileOutputStream(getFile(path)));
			out.writeObject(map);
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new FileNotFoundException("未找到文件");
		}
		catch (IOException e) {
			e.printStackTrace();
			log.error("writeObjectTfile:失败 " + FormatExceptionStack.formatException(e));
			throw new IOException("写对象到文件失败");
		}
		finally {
			flush(out);
			close(out);
		}
	}

	private static void zip(String zipFileName, File inputFile) throws IOException {
		ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
		zip(out, inputFile, "");// 递归压缩方法
		close(out);
	}

	/**
	 * 压缩文件<br>
	 * 
	 * @param zipFileName
	 *            保存的压缩包文件路径
	 * @param inputFile
	 *            需要压缩的文件夹或者文件路径
	 * @exp z.zip("E:\\测试文件\\zip\\myfiles.zip", "E:\\测试文件\\zip\\a");
	 * @throws Exception
	 */
	public static void zip(String zipFileName, String inputFile) throws IOException {
		zip(zipFileName, getFile(inputFile));
	}

	/**
	 * 递归压缩方法<br>
	 * 
	 * @param out
	 *            压缩包输出流
	 * @param f
	 *            需要压缩的文件
	 * @param base
	 *            压缩的路径
	 * @throws Exception
	 */
	private static void zip(ZipOutputStream out, File f, String base) throws IOException {
		if (f.isDirectory()) {
			File[] fl = f.listFiles();
			out.putNextEntry(new ZipEntry(base + "/"));
			base = base.length() == 0 ? "" : base + "/";
			for (int i = 0; i < fl.length; i++) {
				zip(out, fl[i], base + fl[i].getName());
			}
		} else {
			out.putNextEntry(new ZipEntry(base));
			FileInputStream in = new FileInputStream(f);
			int b;
			while ((b = in.read()) != -1) {
				out.write(b);
			}
			flush(out);
			close(in);
		}
	}
}
